#include "map.h"
#include "mapObject.h"
#include "gameEngine.h"
#include "action.h"
#include "dialog.h"

mapCtr::mapCtr():   width( 90 ),
                    height( 50 )
{
    size = width * height - width / 2;
    if( (width % 2) ) size --;
    data = new cell_t[size];
    for(unsigned int i = 0; i < size; i++){
        data[i] = CELL_NORMAL;
    }

    resetMP();
    resetNP();
    resetDP();
}
mapCtr::~mapCtr(){
    delete[] data;
    for( vector<mapObject*>::iterator i = objects.begin(); i < objects.end(); i++){
        delete (*i);
    }
    neutralMutex.lock();
        for( vector<actor*>::iterator i = neutrals.begin(); i < neutrals.end(); i++){
            delete (*i);
        }
    neutralMutex.unlock();
    dialogMutex.lock();
        for( vector<dialog*>::iterator i = dials.begin(); i < dials.end(); i++){
            delete (*i);
        }
    dialogMutex.unlock();
}

int mapCtr::getWidth(){
    return width;
}
int mapCtr::getHeight(){
    return height;
}

void mapCtr::setParentEngine(gameEngine* ge){
    parentEngine = ge;
}

cell_t* mapCtr::getCell( int x, int y){
    unsigned int pos = x * (height - 1) + x / 2 + y;
    return &data[pos];
}
cell_t* mapCtr::getCell( coord_t coord ){
    return getCell( coord.X_coord, coord.Y_coord );
}
bool mapCtr::isCellWalkable( int x, int y){
    if( x < 0 || y < 0 ) return false;
    if( x >= width ) return false;
    if( x % 2 ){
        if( y >= height ) return false;
    }else if( y >= height - 1 ) return false;
    cell_t* cell = getCell(x,y);
    if( getStatus( CELL_OBSTACLE, *cell ) || getStatus( CELL_OCCUPIED, *cell ) ) return false;
    return true;
}
bool mapCtr::isCellWalkable( coord_t coord ){
    return isCellWalkable( coord.X_coord, coord.Y_coord );
}
void mapCtr::setCell( cell_t var, int x, int y){
    unsigned int pos = x * (height - 1) + x / 2 + y;
    data[pos] = var;
}

void mapCtr::addObject( mapObject* obj ){
    obj->setParentMap(this);
    if( obj->getType() == MOB_ACTOR ){
        if( ((actor*)obj)->getFaction() == FAC_NEUTRAL ){
            neutrals.push_back( (actor*)obj );
            parentEngine->NActorsMutex.lock();
                parentEngine->stackNActorW( (actor*)obj );
            parentEngine->NActorsMutex.unlock();
        }else{
            objects.push_back( obj );
        }
    }else{
        objects.push_back( obj );
    }
}

void mapCtr::addDialog( dialog* dial ){
    dials.push_back( dial );
}

unsigned int mapCtr::countMain(){
    return objects.size();
}
void mapCtr::resetMP(){
    mainPtr = objects.begin();
}
mapObject* mapCtr::curMP(){
    if( countMain() == 0 ) return NULL;
    return (*mainPtr);
}

mapObject* mapCtr::nextMP(){
    if( countMain() == 0 ) return NULL;
    mainPtr++;
    if( mainPtr == objects.end() ) return NULL;
    return (*mainPtr);
}

unsigned int mapCtr::countNeutral(){
    return neutrals.size();
}
void mapCtr::resetNP(){
    neutralPtr = neutrals.begin();
}
actor* mapCtr::curNP(){
    if( countNeutral() == 0 ) return NULL;
    return (*neutralPtr);
}
actor* mapCtr::nextNP(){
    if( countNeutral() == 0 ) return NULL;
    neutralPtr++;
    if( neutralPtr == neutrals.end() ) return NULL;
    return (*neutralPtr);
}

unsigned int mapCtr::countDials(){
    return dials.size();
}
void mapCtr::resetDP(){
    dialogPtr = dials.begin();
}
dialog* mapCtr::curDP(){
    if( countDials() == 0 ) return NULL;
    return (*dialogPtr);
}
dialog* mapCtr::nextDP(){
    if( countDials() == 0 ) return NULL;
    dialogPtr++;
    if( dialogPtr == dials.end() ) return NULL;
    return (*dialogPtr);
}

void mapCtr::execActions(){
    actor* act;
    action* acti;
    dialog* dia;

    neutralMutex.lock();
        parentEngine->NActorsMutex.lock();
            resetNP();
            if((act = curNP())){
                do{
                    if( (acti = act->getCurAction())){
                        if( acti->getProgress() == 1 ){
                            act->endAction();
                            parentEngine->stackNActorW(act);
                        }
                    }
                }while( (act = nextNP()) );
            }
        parentEngine->NActorsMutex.unlock();
    neutralMutex.unlock();

    dialogMutex.lock();
        resetDP();
        if( (dia = curDP()) ){
            do{
                if( dia->getCreationTime() + DIALOG_LIFE < clock() ){
                    dials.erase( dialogPtr );
                    delete dia;
                }
            }while( (dia = nextDP()) );
        }
    dialogMutex.unlock();
}

direc_t dir_opposite(direc_t input){
    return (input + 3) % 6;
}

direc_t dir_rotate(direc_t input,int num){
    int res;
    if( num < 0 && -num > input ){
        res = (char)(6 - ((-(char)num - input)%6));
        return res;
    }
    res = (input + num) % 6;
    return res;
}

int getGridY(int y,int x){
    unsigned int parity;
    if( x % 2 ) parity = 0;
    else parity = 1;
    return 2*y+parity;
}

bool getStatus(unsigned char status,cell_t cell){
    return cell&status;
}
void setStatus(unsigned char status,cell_t* cell,bool value){
    if( value ){
        (*cell) |= status;
    }else{
        (*cell) &= ~status;
    }
}
